import backtrader as bt
import pandas as pd
import datetime
from config.indexcode import code_list, currency_list
from sklearn.preprocessing import MinMaxScaler
from utils.cacular_date import cacular_date, getEveryDay
import pickle, numpy as np


scaler = MinMaxScaler(feature_range=(0, 100))

class MyStrategy(bt.Strategy):
    params=(('maperiod',15),
            ('printlog',False),)

    def __init__(self, currency, date_list):
        #指定价格序列
        self.dataclose=self.datas[0].close
        self.dataopen=self.datas[0].open
        self.date_=self.datas[0].datetime.date
        self.file_list = currency_list + code_list
        self.date_list = date_list
        self.currency_df = self.get_df(currency, date_list, type="normal")
        self.df_code = dict()
        for code in self.file_list:
            self.df_code[code] = self.get_df(code, date_list)
        with open('data/temp/Bumblebee.model', 'rb') as fr:
            self.model = pickle.load(fr)
        self.ordersList = list()

    def get_df(self, filename, date_list, type="minmax"):
        file = "data/%s.csv" % filename.replace('/', '')
        df = pd.read_csv(file)
        df = df.set_index('open_time')
        if 'change' not in df.columns:
            df['change'] = (df['close'] - df['close'].shift()) / df['close'].shift() * 100
        # 选取固定的列进行规范化
        dfchange = df.loc[:, 'change']
        dfchange.index = df.index
        df = df.loc[:, ['open','high','low','close','volume']]
        for date_ in date_list:
            if date_ not in df.index:
                tmp = df.mode(axis=0, numeric_only=True)
                tmp['open_time'] = date_
                tmp = pd.DataFrame(tmp.to_dict(), index=[0]).set_index('open_time')
                df = pd.concat([df, tmp])
                df.sort_values("open_time", inplace=True)
        df = df[date_list[0]:date_list[-1]]
        dfchange = dfchange[date_list[0]:date_list[-1]]
        if type == "minmax":
            df_list = df.values
            df_index = df.index
            df_list = scaler.fit_transform(df_list)
            df = pd.DataFrame(df_list, index=df_index, columns=['open','high','low','close','volume'])
        df = pd.concat([df, dfchange], axis=1)
        df = df.fillna(0)
        df = df[~df.index.duplicated(keep='first')]
        return df


    def get_12_data(self, date_list):
        index_file_list = list()
        for code in self.file_list:
            df_ = self.df_code[code]
            dfs_ = df_[date_list[0]: date_list[-1]]

            # 这里的result_是单个文件的12天的特征信息，需要把几个指标文件组合到一起。
            index_file_list.extend(dfs_.values.tolist())

        return index_file_list


    #策略核心，根据条件执行买卖交易指令（必选）
    def next(self):
        date_now = self.date_()
        date_before = cacular_date(date_now, -12)
        if date_before not in self.date_list:
            print("当前日期%s, %s 这里还没有数据" % (date_now, date_before))
            return
        if cacular_date(date_now, 5) > self.date_list[-1]:
            print("预计卖出日期超出了边界")
            print("当前的日期为%s, 预计卖出日期为%s" % (date_now, cacular_date(date_now, 5)))
            return

        # last_year_date = cacular_date(date_now, -365)
        # lastyear_datelist = getEveryDay(last_year_date, cacular_date(date_now, -1))
        # lastyear_main_df = self.currency_df[lastyear_datelist[0]:lastyear_datelist[-1]]

        next_datelist = getEveryDay(date_before, cacular_date(date_now, -1))

        df_list = self.get_12_data(next_datelist)
        df_array = np.array(df_list)
        df_array = df_array.ravel().reshape(1, -1)

        f = self.model.predict(df_array)
        print(f[0])
        if f[0] == 1.0:
            self.ordersList.append(cacular_date(date_now, 5))
            print("购买, 当前买入日期为%s, 当前买入价格为%s" % (date_now, self.dataclose[0]))
            self.order = self.buy(size=1000)

        if str(date_now) in self.ordersList:
            print("--"*20)
            print("卖出, 当前卖出日期为%s, 当前卖出价格为%s" % (date_now, self.dataclose[0]))
            self.order = self.sell(size=1000)


    #交易记录日志（可省略，默认不输出结果）
    def log(self, txt, dt=None,doprint=False):
        if self.params.printlog or doprint:
            dt = dt or self.datas[0].datetime.date(0)
            print(f'{dt.isoformat()},{txt}')

    #记录交易执行情况（可省略，默认不输出结果）
    def notify_order(self, order):
        # 如果order为submitted/accepted,返回空
        if order.status in [order.Submitted, order.Accepted]:
            return
        # 如果order为buy/sell executed,报告价格结果
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log(f'买入:\n价格:{order.executed.price},\
                成本:{order.executed.value},\
                手续费:{order.executed.comm}')
                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
            else:
                self.log(f'卖出:\n价格：{order.executed.price},\
                成本: {order.executed.value},\
                手续费{order.executed.comm}')
            self.bar_executed = len(self)

        # 如果指令取消/交易失败, 报告结果
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('交易失败')
        self.order = None

    #记录交易收益情况（可省略，默认不输出结果）
    def notify_trade(self,trade):
        if not trade.isclosed:
            return
        self.log(f'策略收益：\n毛收益 {trade.pnl:.2f}, 净收益 {trade.pnlcomm:.2f}')

    #回测结束后输出结果（可省略，默认输出结果）
    def stop(self):
        self.log('(MA均线： %2d日) 期末总资金 %.2f' %
                 (self.params.maperiod, self.broker.getvalue()), doprint=True)

if __name__ == '__main__':
    cerebro = bt.Cerebro()
    currency = "ETHUSDT"
    dataframe = pd.read_csv("data/%s.csv" % currency)

    dataframe['open_time'] = pd.to_datetime(dataframe['open_time'])
    dataframe.set_index('open_time', inplace=True)
    dataframe['openinterest'] = 0
    brf_ = bt.feeds.PandasData(
        dataname=dataframe,
        fromdate=datetime.datetime(2017, 11, 15),
        todate=datetime.datetime(2020, 1, 25)
    )


    # 获取当昨天的日期
    # date_yestoday = cacular_date(datetime.date.today(), -1)
    # date_start = cacular_date(date_yestoday, -365)
    # date_list = getEveryDay(date_start, date_yestoday)
    date_yestoday = cacular_date(datetime.date.today(), -1)
    date_start = cacular_date(date_yestoday, -1100)
    temp_yesday = cacular_date(datetime.date.today(), -300)
    date_list = getEveryDay(date_start, temp_yesday)

    cerebro.addstrategy(MyStrategy, currency, date_list)
    cerebro.adddata(brf_)
    cerebro.broker.setcash(1000000.0)
    cerebro.broker.setcommission(0.002)
    print('启动资金: %.2f' % cerebro.broker.getvalue())
    cerebro.run()
    cerebro.plot()
